Skip to content

Stage uploads on the cache filesystem - #13814

Draft
abidlabs wants to merge 2 commits into
mainfrom
fix/issue-13722-upload-route-returns-cache-path-before-cross-fil
Draft

Stage uploads on the cache filesystem#13814
abidlabs wants to merge 2 commits into
mainfrom
fix/issue-13722-upload-route-returns-cache-path-before-cross-fil

Conversation

@abidlabs

@abidlabs abidlabs commented Sep 2, 2026

Copy link
Copy Markdown
Member

Description

Multipart uploads were staged in the operating-system temp directory. When GRADIO_TEMP_DIR was on another filesystem, the final rename failed and /upload returned the destination before a background copy published it.

Stage upload temp files in Gradio's configured upload directory so the final content-addressed rename stays on one filesystem and completes before the response. This avoids both the publication race and an extra synchronous copy of large uploads.

Closes: #13722

AI Disclosure

  • I used AI to reproduce the race, draft the fix and regression test, and prepare this PR description. I self-reviewed every changed line and verified the behavior locally.
  • I did not use AI

🎯 PRs Should Target Issues

This PR targets #13722. I checked for overlapping open PRs; none were found.

Testing and Formatting Your Code

  • python3 -m pytest test/test_routes.py::TestRoutes::test_upload_is_staged_in_custom_upload_path -q
  • ruff check --no-cache gradio/route_utils.py test/test_routes.py
  • ruff format --check gradio/route_utils.py test/test_routes.py
  • ty check --python /Library/Frameworks/Python.framework/Versions/3.14/bin/python3 gradio/route_utils.py
  • Local 32 MiB HTTP probe: before, the returned path was missing and an immediate Range GET returned 403; after, the path existed and the Range GET returned 206 with 1,024 bytes.

Minimal reproduction:

import errno
import os
import shutil
import tempfile
import time
from pathlib import Path

CACHE_DIR = Path(tempfile.gettempdir()) / "gradio-upload-race"
os.environ["GRADIO_TEMP_DIR"] = str(CACHE_DIR)

import gradio as gr
from gradio import route_utils, routes

original_rename = os.rename

def cross_filesystem_rename(source, destination):
    source = Path(source).resolve()
    destination = Path(destination).resolve()
    if destination.is_relative_to(CACHE_DIR.resolve()) and not source.is_relative_to(CACHE_DIR.resolve()):
        raise OSError(errno.EXDEV, "simulated cross-filesystem upload")
    return original_rename(source, destination)

def delayed_move(sources, destinations):
    time.sleep(5)
    for source, destination in zip(sources, destinations, strict=False):
        Path(destination).parent.mkdir(parents=True, exist_ok=True)
        shutil.move(source, destination)

route_utils.os.rename = cross_filesystem_rename
routes.move_uploaded_files_to_cache = delayed_move

with gr.Blocks() as demo:
    gr.File()

demo.launch()

The reproduction files were not committed.

@gradio-pr-bot

Copy link
Copy Markdown
Collaborator

🦄 change detected

This Pull Request includes changes to the following packages.

Package Version
gradio patch

  • Stage uploads on the cache filesystem

Something isn't right?

  • Maintainers can change the version label to modify the version bump.
  • If the bot has failed to detect any changes, or if this pull request needs to update multiple packages to different versions or requires a more comprehensive changelog entry, maintainers can update the changelog file directly.

@gradio-pr-bot

gradio-pr-bot commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

🪼 branch checks and previews

Name Status URL
Spaces ready! Spaces preview
Website ready! Website preview
🦄 Changes detected! Details

Install Gradio from this PR

pip install https://huggingface.co/buckets/gradio/pypi-previews/resolve/a3716939b6cdcab788a7a295bfa2abaaa87e2196/gradio-6.26.0-py3-none-any.whl

Install Gradio Python Client from this PR

pip install "gradio-client @ git+https://github.com/gradio-app/gradio@a3716939b6cdcab788a7a295bfa2abaaa87e2196#subdirectory=client/python"

Import Gradio JS Client from this PR via CDN

import { Client } from "https://huggingface.co/buckets/gradio/npm-previews/resolve/a3716939b6cdcab788a7a295bfa2abaaa87e2196/browser.js";

@abidlabs

abidlabs commented Sep 2, 2026

Copy link
Copy Markdown
Member Author

Before / after Spaces

  • Before fix — latest released Gradio, upload publication race present
  • After fix — wheel from this PR, upload immediately readable

Both Spaces run the same app with multipart staging on /dev/shm and GRADIO_TEMP_DIR on /tmp.

Space Upload Immediate Range: bytes=0-1023
Before HTTP 200 HTTP 403, 151-byte error body
After HTTP 200 HTTP 206, 1,024 bytes

@abidlabs
abidlabs requested review from dawoodkhan82 and hysts and a balanced review from Copilot September 2, 2026 22:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The hand-authored changeset must be removed per repository policy.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Stages multipart uploads on Gradio’s cache filesystem to ensure atomic publication.

Changes:

  • Configures multipart temporary-file placement.
  • Adds a regression test for cross-filesystem uploads.
  • Adds a manual changeset.
File summaries
File Description
gradio/route_utils.py Stages uploads in the configured upload directory.
test/test_routes.py Tests synchronous upload publication.
.changeset/seven-bears-stick.md Adds a prohibited manual changeset.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +1 to +5
---
"gradio": patch
---

fix:Stage uploads on the cache filesystem
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Upload route returns cache path before cross-filesystem move completes

3 participants